package crypt

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
)

/*
AES（Advanced Encryption Standard）是一种对称加密算法，其加解密原理如下：

1. 基本概念

  - 对称加密：加密和解密使用相同的密钥
  - 分组密码：将明文分成固定大小的块（AES为128位）进行加密
  - 支持密钥长度：128位、192位或256位

2. 加密过程

  - 密钥扩展 ：将初始密钥扩展为多轮加密所需的轮密钥
  - 初始轮 ：
  - 将明文块与第一轮密钥进行异或操作（AddRoundKey）
  - 主轮（9/11/13轮） ：
    1. SubBytes ：通过S盒进行非线性字节替换
    2. ShiftRows ：对状态矩阵的行进行循环移位
    3. MixColumns ：对状态矩阵的列进行线性变换
    4. AddRoundKey ：与当前轮密钥进行异或
  - 最终轮 ：
  - 省略MixColumns步骤

3. 解密过程

  - 逆向执行加密的各步骤
  - 使用逆变换：InvSubBytes、InvShiftRows、InvMixColumns

4. 工作模式 （如CBC模式）

  - 每个明文块先与前一个密文块异或后再加密
  - 需要初始化向量(IV)来保证相同明文产生不同密文

5. 安全性

  - 基于代换-置换网络(SPN)结构
  - 经过严格数学分析和广泛实践验证
  - 目前没有已知的有效攻击方法

本代码使用的是AES/CBC/PKCS5Padding模式，即：

- 分组大小：128位
- 工作模式：CBC（密码块链接）
- 填充方式：PKCS5（当数据不是块大小的整数倍时自动填充）
*/
type Aes struct {
	B64Key string
}

func (this *Aes) GetB64KeyByte() []byte {
	return []byte(this.B64Key)
}

// 生成AES-128密钥
func (this *Aes) GenerateBase64AESKey() (err error) {
	key := make([]byte, 16)
	_, err = rand.Read(key)
	if err != nil {
		return
	}
	this.B64Key = base64.StdEncoding.EncodeToString(key)
	return
}

// PKCS5填充
func (this *Aes) pkcs5Padding(contentByte []byte, blockSize int) []byte {
	padding := blockSize - len(contentByte)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(contentByte, padtext...)
}

// PKCS5去填充
func pkcs5UnPadding(contentByte []byte) []byte {
	length := len(contentByte)
	unpadding := int(contentByte[length-1])
	return contentByte[:(length - unpadding)]
}

// 使用AES-128 CBC模式加密数据
func (this *Aes) Encrypt(content string, iv string) ([]byte, error) {
	contentByte := []byte(content)
	block, err := aes.NewCipher(this.GetB64KeyByte())
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	contentByte = this.pkcs5Padding(contentByte, blockSize)
	ciphertext := make([]byte, len(contentByte))
	mode := cipher.NewCBCEncrypter(block, []byte(iv))
	mode.CryptBlocks(ciphertext, contentByte)
	return ciphertext, nil
}

// 使用AES-128 CBC模式解密数据
func (this *Aes) Decrypt(contentByte []byte, iv string) ([]byte, error) {
	block, err := aes.NewCipher(this.GetB64KeyByte())
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, []byte(iv))
	mode.CryptBlocks(contentByte, contentByte)
	contentByte = pkcs5UnPadding(contentByte)
	return contentByte, nil
}

func NewAes() *Aes {
	return &Aes{}
}
